<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
         // 尝试双向数据绑定

        // 首先定义一个方法用来数据劫持
        function observe(obj) {
            // 判断是否为obj
            if (!isObject(obj)) {
                // 抛出错误
                throw new TypeError()
            }
            // 在这里对obj进行循环
            // 因为Object.defineProperty 需要对key值进行监听
            Object.keys(obj).forEach(key => {
                // 创建一个内部变量
                let internalValue = obj[key]

                // 创建一个dep 实例
                let dep = new Dep()
                // 使用Object.defineProperty 进行数据监听
                // 在这里对gei 和set 方法进行重构
                Object.defineProperty(obj, key, {
                    // 获取数据并返回
                    get() {

                        dep.depend()
                        console.log(activeUpdate,'更新状态activeUpdate');
                        // 将获取到的数据进行返回
                        return internalValue
                    },
                    // 设置数据
                    set(newValue) {
                        console.log(newValue,'改变的值newValue');
                        const isChanged = internalValue !== newValue
                        if (isChanged) {
                            internalValue = newValue
                            dep.notify()
                        }
                    }
                })
            })
        }

        // 在这里对dep进行声明创建

        window.Dep = class Dep {
            constructor() {
                // 声明一个变量 订阅者
                this.subscribers = new Set()
            }
            // 同时此方法还应该有 依赖和通知

            depend() {
                // 通知判断状态
                if (activeUpdate) {
                    // 将当前活动更新注册为订阅者  
                    this.subscribers.add(activeUpdate)
                }
            }
            // 通知
            notify() {
                // 运行所有订阅函数
                console.log(this.subscribers,'所有的属性');
                this.subscribers.forEach(subscriber => {
                    subscriber()
                    console.log(subscriber,'方法');
                })
            }
        }

        let activeUpdate
        // 一个自动执行的方法
        function autorun(update) {
            function wrappedUpdate() {
                activeUpdate = wrappedUpdate
                // 再此调用 输入的方法也就是对响应式触发
                update()
                activeUpdate = null
            }
            wrappedUpdate()
        }

        // 一个判断是否为obj的方法
        function isObject(obj) {
            return typeof obj === 'object'
                && !Array.isArray(obj)
                && obj !== null
                && obj !== undefined
        }

        // 定义一个对象
        let state = {
            count: 0
        }
        
        /**当执行这个方法时，会首先判断是否为obj 如果是则对state 键值对(属性名)进行循环
         *然后为每个属性名创建一个dep class 然后用 object.defineProperty进行监听
         本质上当调用或者修改当前数据的时候都会进行监听，这也就是为什么activeUpdate 存在的必要
         defineProperty 方法 首先会在get方法也就是获取值的时候 调用 depend方法 将参数注册到订阅者里
        同时在set 方法时判断数据是否改变同时通知notify 来进行操作 也就是运行 autorun 方法即自动执行
          
        
        **/
        observe(state)

        autorun(() => {
            window.console.log(`count is: ${state.count}`)
        })
        // console.log(state.count);
        state.count++
    </script>
</body>
</html>